package common.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import common.db.JdbcSource;
import common.db.jdbc.Jdbc;

import core.config.Env;
import core.exception.ThrowableUtil;

/**
 * DB検索処理クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class SelectorImpl implements Selector {

	/** セレクタ設定値(MaxRows) */
	private static final String ENV_SELECT_MAX_ROWS = "Selector.MaxRows";
	/** セレクタ設定値(FetchSize) */
	private static final String ENV_SELECT_FETCH_SIZE = "Selector.FetchSize";

	/** 取得サイズデフォルト */
	private static final int DEFAULT_FETCH_SIZE = Env.getEnv(ENV_SELECT_FETCH_SIZE, 100);
	/** 最大レコードデフォルト */
	private static final int DEFAULT_MAX_ROWS = Env.getEnv(ENV_SELECT_MAX_ROWS, 0);

	/** 取得サイズ */
	private int fetchsize = 0;
	/** 最大レコード */
	private int maxrows = 0;
	/** コネクション */
	private Connection con = null;

	/**
	 * コネクション設定
	 *
	 * @param val コネクション
	 */
	@Override
	public void setConnection(final Connection val) {
		this.con = val;
	}

	/**
	 * 取得サイズ設定
	 *
	 * @param size サイズ
	 */
	@Override
	public void setFetchSize(final int size) {
		if (0 < size) {
			this.fetchsize = size;
		}
	}

	/**
	 * 最大レコード数設定
	 *
	 * @param size レコード数
	 */
	@Override
	public void setMaxRows(final int size) {
		if (0 < size) {
			this.maxrows = size;
		}
	}

	/**
	 * データ検索
	 *
	 * @param sels クエリオブジェクト
	 * @return レコードが存在した場合 true を返す。
	 */
	@Override
	public boolean search(final SelectQuery... sels) {
		if (this.con != null) {
			return search(this.con, sels);
		}

		try (Jdbc conn = JdbcSource.getConnection()) {
			return search(conn, sels);
		}
	}

	/**
	 * 結果セットの取得と格納
	 *
	 * @param conn コネクションオブジェクト
	 * @param sels SelectQueryオブジェクト
	 * @return レコードが存在した場合 true を返す。
	 */
	private boolean search(final Connection conn, final SelectQuery... sels) {
		boolean ret = false;
		if (sels != null) {
			for (final SelectQuery sel : sels) {
				if (search(sel, conn)) {
					ret = true;
				}
			}
		}
		return ret;
	}

	/**
	 * 結果セットの取得と格納
	 *
	 * @param sel SelectQueryオブジェクト
	 * @param conn コネクションオブジェクト
	 * @return レコードが存在した場合 true を返す。
	 */
	private boolean search(final SelectQuery sel, final Connection conn) {

		try (PreparedStatement psmt =
				QueryUtil.createStatement(conn, sel.makeQuery(), sel.makeParam())) {
			if (psmt != null) {
				setStatementParameters(psmt);

				try (ResultSet rs = psmt.executeQuery()) {
					return sel.callback(rs);
				}
			}
			return false;
		} catch (SQLException ex) {
			ThrowableUtil.warn(ex);
			return false;
		}
	}

	/**
	 * ステートメントパラメタ設定
	 *
	 * @param stmt ステートメント
	 * @throws SQLException SQL例外
	 */
	private void setStatementParameters(final Statement stmt) throws SQLException {
		// フェッチサイズ
		if (0 < this.fetchsize) {
			stmt.setFetchSize(this.fetchsize);
		} else if (0 < DEFAULT_FETCH_SIZE) {
			stmt.setFetchSize(DEFAULT_FETCH_SIZE);
		}
		// 最大レコード
		if (0 < this.maxrows) {
			stmt.setMaxRows(this.maxrows);
		} else if (0 < DEFAULT_MAX_ROWS) {
			stmt.setMaxRows(DEFAULT_MAX_ROWS);
		}
	}
}
